package com.seanLab.service;

import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.mongodb.BasicDBObject;
import com.mongodb.BulkWriteOperation;
import com.mongodb.BulkWriteResult;
import com.mongodb.MongoException;
import com.seanLab.configuration.ModelConfig;
import com.seanLab.configuration.MongoConfig;
import com.seanLab.configuration.StaticConfig;
import com.seanLab.domain.*;
import com.seanLab.dto.MetricsDto;
import com.seanLab.dto.SuggestArticleKeywordsDto;
import com.seanLab.dto.SuggestModelArticleDto;
import com.seanLab.repository.ArticleRepo;
import com.seanLab.repository.EditorSessionRepo;
import com.seanLab.repository.ImageRepo;
import com.seanLab.repository.SpecialImageRepo;
import com.seanLab.tool.data.DataExtractor;
import com.seanLab.tool.TagSuggestor;
import com.seanLab.tool.es.ESImageSearch;
import com.seanLab.util.TopicPair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.io.*;
import java.util.*;

/**
 * Created by ailly on 17-10-9.
 */
@Service
@ConfigurationProperties(prefix = "com.dasLab.dataService")
public class DataService {
    @Autowired
    private ArticleRepo articleRepo;

    @Autowired
    private ImageRepo imageRepo;

    @Autowired
    private SpecialImageRepo specialImageRepo;

    @Autowired
    private ESImageSearch esImageSearch;

    @Autowired
    private EditorSessionRepo editorSessionRepo;

    @Autowired
    private TagSuggestor tagSuggestor;

    @Autowired
    private ModelConfig modelConfig;

    @Autowired
    private MongoConfig mongoConfig;

    @Autowired
    private MongoTemplate mongoTemplate;

    private int threadNum;

    @PostConstruct
    public void init() {
        this.esImageSearch.initConnection();
        this.tagSuggestor.loadModel(modelConfig.getModelPath());
        this.tagSuggestor.loadLdaModel(modelConfig.getLdaModelPath());
    }
    public int getThreadNum() {
        return threadNum;
    }

    public void setThreadNum(int threadNum) {
        this.threadNum = threadNum;
    }

    private Article extractKeywordsFromArticle(Article article) {
        Article tmpArticle = article;
        //@吴小波 TagSuggestor应返回该文章对应的关键词. e.g., articleTagList
        //@吴小波 填充以下代码
        //TagSuggestor t1 = new TagSuggestor();
        //SuggestModelArticleDto suggestModelArticleDto = DataExtractor.extractModelArticleFromArticle(article);
        //List<String> keywords = t1.doKeyword(suggestModelArticleDto);

        //fake data
        //List<String> keywords = new ArrayList<String>(Arrays.asList("keyword1", "keyword2"));

        SuggestModelArticleDto suggestModelArticleDto = DataExtractor.extractModelArticleWithoutImgsFromArticle(article);
        List<SuggestArticleKeywordsDto> keywords = this.tagSuggestor.doKeyword(suggestModelArticleDto);

        tmpArticle.setKeywords(keywords);
        return tmpArticle;
    }

    private List<Image> extractImgTagsFromArticle(Article article, List<Image> imageList) {
        List<Image> tmpImgList = new ArrayList<Image>(imageList);

        SuggestModelArticleDto suggestModelArticleDto = DataExtractor.extractModelArticleFromArticle(article, imageList);

        long start = System.currentTimeMillis();

        List<List<TagInfo>> articleTagList = this.tagSuggestor.doTag(suggestModelArticleDto);

        long end = System.currentTimeMillis();
        long cost = end - start;

        for (Image tmpImg : tmpImgList) {
            int index = tmpImgList.indexOf(tmpImg);
            tmpImg.setImgTagList(articleTagList.get(index));

        }
        return tmpImgList;
    }

    public void extractImgTagsFromExistArticleTest() {
        //取出所有的article
        //计时
        long start = System.currentTimeMillis();

//        List<Article> existArticleList = this.articleRepo.findByExtractImgFlag();
        List<Article> existArticleList = this.articleRepo.findTop1000ByExtractImgFlag(new PageRequest(0 , 10));

        long end = System.currentTimeMillis();
        long cost = end - start;
        System.out.println("待抽取标签文章查询完毕，耗时： " + cost + "ms");

        //TODO 多线程测试
//        BlockingDeque<Article> tmpArticleQueue = new LinkedBlockingDeque<>(existArticleList);
//
//        ExecutorService exec = Executors.newCachedThreadPool();
//        for (int i = 0; i < this.threadNum; i++) {
//            exec.execute(new RunImageTagging(tmpArticleQueue, this.articleRepo, this.imageRepo, this.tagSuggestor) );
//        }

        start = System.currentTimeMillis();

        for (Article tmpExistArticle : existArticleList) {
            //取出一个article中的imageList
            List<Image> tmpImageList = new ArrayList<>();
            for (String tmpImgID : tmpExistArticle.getImgID()) {
                Image tmpImage = this.imageRepo.findByImgID(tmpImgID);
                tmpImageList.add(tmpImage);
            }
            //抽取图片标签
            List<Image> tmpImageListWithTags = this.extractImgTagsFromArticle(tmpExistArticle, tmpImageList);
            //更新图片的标签
            this.imageRepo.save(tmpImageListWithTags);

            //更新文章关键词
            Article tmpArticleWithKeywords = this.extractKeywordsFromArticle(tmpExistArticle);
            tmpArticleWithKeywords.setExtractImgFlag(1);
            this.articleRepo.save(tmpArticleWithKeywords);
        }

        end = System.currentTimeMillis();
        cost = end - start;
        System.out.println("抽取图片标签完毕，耗时： " + cost + "ms");

    }

    //将新增的image记录同步至ES
    public int loadImagesToES() {
        ESImageSearch esImageSearch = this.esImageSearch;
        //esImageSearch.initConnection();

        //记录操作时间
        long start = System.currentTimeMillis();

        //TODO 取出flag为0的图片记录（且imgTagList不为空）
//        List<Image> ilist = this.imageRepo.findByImgTagListAndEsFlag(new ArrayList<TagInfo>());

        List<Image> imageList = this.imageRepo.findTop10000ByImgTagListAndEsFlag(new ArrayList<TagInfo>(), new PageRequest(0 , 10000));

        System.out.println("LoadToES: Query time: " + (System.currentTimeMillis() - start) + "ms. ");

        start = System.currentTimeMillis();
        if (imageList.isEmpty()) {
            return 1;
        }

        int tagSize = 5;

        List<Image> esList = new ArrayList<>();
//        List<Image> olist = new ArrayList<>();

        //剔除没有topic的图片记录
        //删除imgType不为0的图片
        for (Image image : imageList) {
            if (image.getImgType() != 0 || (image.getTopTopics().get(0) == 0 && image.getTopTopics().get(1) == 1)) {
                image.setEsFlag(-1);
            } else {
                image.setEsFlag(1);
                esList.add(image);
            }
        }

//        for (Iterator<Image> iter = imageList.listIterator(); iter.hasNext(); ) {
//            Image i = iter.next();
//            if (i.getImgType() != 0 || (i.getTopTopics().get(0) == 0 && i.getTopTopics().get(1) == 1)) {
//                olist.add(i);
//                iter.remove();
//            } else {
//                if (i.getImgTagList().size() >= 5)
//                    i.setImgTagList(i.getImgTagList().subList(0,tagSize));
//            }
//        }
        System.out.println("LoadToES: Process time: " + (System.currentTimeMillis() - start) + "ms. ");

        start = System.currentTimeMillis();
        //TODO @张帆 es相关代码需根据image修改
//        esImageSearch.insertImageList(ilist);
        //TODO esImageSearch.insertImageListBulkWithTopics(ilist);
        if (!esList.isEmpty()) {
            esImageSearch.insertImageListBulk(esList);
        }
        System.out.println("LoadToES: Insert time: " + (System.currentTimeMillis() - start) + "ms. ");

        start = System.currentTimeMillis();
        //TODO 导入es后更新flag
        bulkUpdateEsFlag(mongoConfig.getImage(), imageList);
//        for (Image i : imageList) {
//            i.setEsFlag(1);
//        }
//        for (Image o : olist) {
//            o.setEsFlag(-1);
//        }
//
//        if (!imageList.isEmpty()) {
//            this.imageRepo.save(imageList);
//        }
//        if (!olist.isEmpty()) {
//            this.imageRepo.save(olist);
//        }
        long end3 = System.currentTimeMillis();
        System.out.println("LoadToES: Update time: " + (end3 - start) + "ms. ");


        return 0;
    }

    public MetricsDto getMetrics(){
        long emptyResultNum = this.editorSessionRepo.countByCoverFlag(0);
        long nonEmptyResultNum = this.editorSessionRepo.countByCoverFlag(1);
        long acceptNum = this.editorSessionRepo.countByAcceptFlag(1);
        long top1AcceptNum = this.editorSessionRepo.countByTop1AcceptFlag(1);
        long top5AcceptNum = this.editorSessionRepo.countByTop5AcceptFlag(1);

        MetricsDto metricsDto = new MetricsDto(emptyResultNum, nonEmptyResultNum, acceptNum, top1AcceptNum, top5AcceptNum);

        return metricsDto;
    }

    public MetricsDto generateReport() {
        String testLable = StaticConfig.testLabel;
        long emptyResultNum = this.editorSessionRepo.countByIsTestFlagAndCoverFlagAndTestLabel(1, 0, testLable);
        long nonEmptyResultNum = this.editorSessionRepo.countByIsTestFlagAndCoverFlagAndTestLabel(1, 1, testLable);
        long acceptNum = this.editorSessionRepo.countByIsTestFlagAndAcceptFlagAndTestLabel(1, 1, testLable);
        long top1AcceptNum = this.editorSessionRepo.countByIsTestFlagAndTop1AcceptFlagAndTestLabel(1, 1, testLable);
        long top5AcceptNum = this.editorSessionRepo.countByIsTestFlagAndTop5AcceptFlagAndTestLabel(1, 1, testLable);

        MetricsDto metricsDto = new MetricsDto(emptyResultNum, nonEmptyResultNum, acceptNum, top1AcceptNum, top5AcceptNum);

        return metricsDto;
    }

    public int generateTopTopics() {
        long start1 = System.currentTimeMillis();
        List<Image> ilist = this.imageRepo.findTop10000ByTopTopics(new ArrayList<>(), new PageRequest(0 , 10000));
        long time1 = System.currentTimeMillis() - start1;
        System.out.println("GenerateTopic: Query time: " + time1 + "ms.");

        if (ilist.isEmpty()) {
            return 1;
        }

        long start3 = System.currentTimeMillis();
        //计算top主题
        for (Image i : ilist) {
            try {
                List<Double> tmp_topics = i.getImgTopic();
                List<Integer> tmp_topTopics = TopicPair.getTopTopics(tmp_topics);
                i.setTopTopics(tmp_topTopics);
            } catch (Exception e) {
                System.out.println("which fengleba==id: " + i.getImgID());
                e.printStackTrace();
            }
        }
        long time3 = System.currentTimeMillis() - start3;
        System.out.println("GenerateTopic: Process time: " + time3 + "ms");

        long start2 = System.currentTimeMillis();
        this.imageRepo.save(ilist);
        long time2 = System.currentTimeMillis() - start2;
        System.out.println("GenerateTopic: Update time: " + time2 + "ms");

        return 0;
    }

    public int loadSpecialImage() {
        JsonParser parser = new JsonParser();
        List<SpecialImage> specialImageList = new ArrayList<>();
        this.specialImageRepo.deleteAll();
        System.out.println("loading");
//        String[] files = new String[] {"中国公司cate.json", "各行业中国公司cate.json", "中国各省公司cate.json", "中国各公司人物cate.json"};
        String[] files = new String[] {"应用科学.json"};
        for (String file : files) {
            try {
                BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("scripts/specialImage/" + file)));
//                BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("scripts/specialImage/" + "wiki_" + file.replace(".json", ".dic"))));
                String line;
                while ((line = reader.readLine()) != null) {
                    JsonObject json = parser.parse(line).getAsJsonObject();
                    SpecialImage image = new SpecialImage();
                    image.setImgID("imageID-" + UUID.randomUUID().toString());
                    image.setImgURL(json.get("imgOriginalURL").getAsString());
                    image.setArticleURL(json.get("articleURL").getAsString());
                    image.setImgOriginalURL(json.get("imgOriginalURL").getAsString());
                    Category category = new Category();
                    category.setName(json.get("imgCategory").getAsString());
                    List<Category> categoryList = new ArrayList<>();
                    categoryList.add(category);
                    image.setImgCategory(categoryList);
                    image.setArticleTitle(json.get("articleTitle").getAsString());
                    image.setImgDescription(json.get("imgDescription").getAsString());
                    TagInfo tagTitle = new TagInfo("tagID-" + UUID.randomUUID().toString(), image.getArticleTitle().toLowerCase(),
                            "wiki-title", 10.0, 0);
                    TagInfo tagDesc = new TagInfo("tagID-" + UUID.randomUUID().toString(), image.getImgDescription().toLowerCase(),
                            "wiki-description", 10.0, 0);
                    List<TagInfo> tagInfoList = new ArrayList<>();
                    tagInfoList.add(tagTitle);
//                    tagInfoList.add(tagDesc); //只加title
                    image.setImgTagList(tagInfoList);
                    image.setImgSource("wiki");
                    image.setImgTopic(new ArrayList<>());
                    image.setTopTopics(new ArrayList<>());

                    specialImageList.add(image);

//                    writer.write(image.getArticleTitle() + "\tdas_wiki\t100\n");
                }
                reader.close();
//                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        this.specialImageRepo.save(specialImageList);
        return 0;
    }

    public int loadSpecialImageToES() {
        long start = System.currentTimeMillis();
        List<SpecialImage> specialImageList = this.specialImageRepo.findTop10000ByImgTagListAndEsFlag(new ArrayList<TagInfo>(), new PageRequest(0 , 10000));
        System.out.println("LoadToES: Query time: " + (System.currentTimeMillis() - start) + "ms. ");
        start = System.currentTimeMillis();
        if (specialImageList.isEmpty()) {
            return 1;
        }
        this.esImageSearch.insertImageListBulk(specialImageList);
        System.out.println("LoadToES: Insert time: " + (System.currentTimeMillis() - start) + "ms. ");

        start = System.currentTimeMillis();
        for (SpecialImage image : specialImageList) {
            image.setEsFlag(1);
        }
//        this.specialImageRepo.save(specialImageList);
        bulkUpdateEsFlag(mongoConfig.getSpecialImage(), specialImageList);
        long end3 = System.currentTimeMillis();
        System.out.println("LoadToES: Update time: " + (end3 - start) + "ms. ");

        return 0;
    }

    public String bulkUpdateEsFlag(String collection, List<? extends AbstractImage> images) {
        BulkWriteOperation bulkOp = mongoTemplate.getCollection(collection).initializeUnorderedBulkOperation();
        for (AbstractImage image : images) {
            bulkOp.find(new BasicDBObject("_id", image.getImgID()))
                    .updateOne(new BasicDBObject("$set", new BasicDBObject("esFlag", image.getEsFlag())));
        }
        try {
            BulkWriteResult result = bulkOp.execute();
            System.out.println(result);
            return result.toString();
        } catch (MongoException e) {
            e.printStackTrace();
        }
        return null;
    }
}
